/* test
 * wget "0.0.0.0:7681/form1?text1=ss&send=uu" -O tmp.txt
 * wget --method=POST 0.0.0.0:7681/forsm1 --body-data="text1=value1&send=value2"  -O tmp.txt
 */
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
#define RING_DEPTH 4096
struct msg {
	void *payload; /* is malloc'd */
	size_t len;
	char binary;
	char first;
	char final;
};
struct pss {
  //http
  struct lws_spa *spa;
  //websocket
	struct lws_ring *ring;
	uint32_t msglen;
	uint32_t tail;
	uint8_t completed:1;
	uint8_t flow_controlled:1;
	uint8_t write_consume_pending:1;
};
static int interrupted, use303;
static const char * const param_names[] = {
  "text1",
  "send",
};
enum enum_param_names {
  EPN_TEXT1,
  EPN_SEND,
};
static const char *server_host = "0.0.0.0:7681";
int port = 7681;
static lws_sorted_usec_list_t sul;
  struct lws_context *context;
  static struct lws *client_wsi;
  static const char *server_address = "0.0.0.0",
  *pro = "lws-mirror-protocol";

static const lws_retry_bo_t retry = {
        .secs_since_valid_ping = 3,
        .secs_since_valid_hangup = 10,
};
void *http_get(struct lws *wsi, void *in, size_t len, void *user)
{
  int n;
  uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE],
  *start = &buf[LWS_PRE], *p = start, *end = &buf[sizeof(buf) - 1];
  const char *val;

  if (strcmp((const char *)in, "/form1"))
    return NULL;
  for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) {
    val = lws_get_urlarg_by_name(wsi, param_names[n],
        (char *)buf, sizeof(buf));
    if (!val)
      lwsl_user("%s: undefined\n", param_names[n]);
    else
      lwsl_user("%s: (len %d) '%s'\n", param_names[n],
          (int)strlen((const char *)buf),buf);
  }
  if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
        (unsigned char *)"after-form1.html", 16, &p, end) < 0)
    return wsi;
  return NULL;
}
static int callback_http(struct lws *wsi,
    enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
  struct pss *pss = (struct pss *)user;
  uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE],
  *start = &buf[LWS_PRE], *p = start, *end = &buf[sizeof(buf) - 1];
  int n;
    int m;

  //printf("%s reason:%d\n", __func__, reason);
  printf("%d-", reason);
  switch (reason) {
  case LWS_CALLBACK_HTTP:
    printf("LWS_CALLBACK_HTTP\n", reason);
    lwsl_err("%s: %s\n", __func__, (const char *)in);
    if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
      if (http_get(wsi, in, len, user)) {
        return -1;
      }
    } else if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
      if (strcmp((const char *)in, "/form1"))
        return 0;
    }
    break;
  case LWS_CALLBACK_HTTP_BODY:
    printf("has body\n");
    if (!pss->spa) {
      pss->spa = lws_spa_create(wsi, param_names,
          LWS_ARRAY_SIZE(param_names), 1024,
          NULL, NULL); /* no file upload */
      if (!pss->spa)
        return -1;
    }
    if (lws_spa_process(pss->spa, in, (int)len))
      return -1;
    break;

  case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
    if (pss->spa && lws_spa_destroy(pss->spa))
      return -1;
    break;

  case LWS_CALLBACK_RECEIVE:
    printf("Received data: %.*s\n", (int)len, (char *)in);
    break;
  case LWS_CALLBACK_HTTP_BODY_COMPLETION:
    lwsl_user("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
    if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
          "text/html", 5, &p, end))
      return 1;
    if (lws_finalize_write_http_header(wsi, start, &p, end))
      return 1;
    strcpy((char *)p, "hello");
    if (lws_write(wsi, (uint8_t *)p, 5, LWS_WRITE_HTTP_FINAL) != 5)
      return 1;
    lws_write(wsi, (uint8_t *)p, 0, LWS_WRITE_HTTP_FINAL);
    if (lws_http_transaction_completed(wsi))
      return -1;
    break;
    //return 0;
  case LWS_CALLBACK_HTTP_WRITEABLE:
    printf("writeable\n");
        strcpy((char *)start, "hello");

    if (lws_write(wsi, (uint8_t *)p, 5, LWS_WRITE_HTTP_FINAL) != 5)
      return 1;

    if (lws_http_transaction_completed(wsi))
      return -1;
    return 0;

    if (lws_http_transaction_completed(wsi))
      return -1;
    /*
    if (lws_http_redirect(wsi, use303 ? HTTP_STATUS_SEE_OTHER :
          HTTP_STATUS_MOVED_PERMANENTLY,
          (unsigned char *)"after-form1.html", 16, &p, end) < 0)
      return -1;
      */
    break;
  case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
    if (pss->spa) {
      lws_spa_destroy(pss->spa);
      pss->spa = NULL;
    }
    break;
  default:
    break;
  }
  return lws_callback_http_dummy(wsi, reason, user, in, len);
}
static void connect_cb(lws_sorted_usec_list_t *_sul)
{
  struct lws_client_connect_info i;

  lwsl_notice("%s: connecting\n", __func__);
  memset(&i, 0, sizeof(i));
  i.context = context;
  i.port = port;
  i.address = server_address;
  i.host = server_host;
  i.origin = i.address;
  i.protocol = pro;
  i.alpn = "h2;http/1.1";
  i.local_protocol_name = "lws-ping-test";
  i.pwsi = &client_wsi;
  i.retry_and_idle_policy = &retry;
  //i.path = "/ws";

  if (!lws_client_connect_via_info(&i))
          lws_sul_schedule(context, 0, _sul, connect_cb,
  5 * LWS_USEC_PER_SEC);
}


static int callback_websocket(struct lws *wsi,
    enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
 // struct pss *pss = (struct pss *)user;
//	const struct msg *pmsg;
//	struct msg amsg;
//	int m, n, flags;

  printf("L%d reason:%d\n", __LINE__, reason);
	switch (reason) {
    case LWS_CALLBACK_CLIENT_ESTABLISHED:
      lwsl_warn("LWS_CALLBACK_ESTABLISHED\n");
      break;

    case LWS_CALLBACK_CLIENT_WRITEABLE:
      lwsl_user("LWS_CALLBACK_SERVER_WRITEABLE\n");
      break;
    case LWS_CALLBACK_CLIENT_RECEIVE:
      printf("Received data: %.*s\n", (int)len, (char *)in);
      break;
    case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
      lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
         in ? (char *)in : "(null)");
      lws_sul_schedule(context, 0, &sul, connect_cb,
          5 * LWS_USEC_PER_SEC);
      break;

    case LWS_CALLBACK_CLOSED:
      printf("L%d\n", __LINE__);
      lwsl_user("LWS_CALLBACK_CLOSED\n");
      break;
    default:
      break;
	}

	return 0;
}
static struct lws_protocols protocols[] = {
  { "http", callback_http, sizeof(struct pss), 0 },
  { "websocket", callback_websocket, sizeof(struct pss), 0 },
  { NULL, NULL, 0, 0 } /* terminator */
};
static const struct lws_http_mount mount = {
  /* .mount_next */              NULL,            /* linked-list "next" */
  /* .mountpoint */               "/",            /* mountpoint URL */
  /* .origin */           "./wsconfig/mount-origin",       /* serve from dir */
  /* .def */                      "index.html",   /* default filename */
  /* .protocol */                 NULL,
  /* .cgienv */                   NULL,
  /* .extra_mimetypes */          NULL,
  /* .interpret */                NULL,
  /* .cgi_timeout */              0,
  /* .cache_max_age */            0,
  /* .auth_mask */                0,
  /* .cache_reusable */           0,
  /* .cache_revalidate */         0,
  /* .cache_intermediaries */     0,
  /* .origin_protocol */          LWSMPRO_FILE,   /* files in a dir */
  /* .mountpoint_len */           1,              /* char count */
  /* .basic_auth_login_file */    NULL,
};

void *wsconfig_httpd_run(void *ptr)
{
  struct lws_context_creation_info info;
  int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;// | LLL_DEBUG;

  lws_set_log_level(logs, NULL);
  lwsl_user(
      "LWS minimal http server POST | visit http://localhost:7681\n");

  memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
  info.port = 7681;
  info.protocols = protocols;
  info.mounts = &mount;
  info.options =
    LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;

  context = lws_create_context(&info);
  if (!context) {
    lwsl_err("lws init failed\n");
    return NULL;
  }
  lws_sul_schedule(context, 0, &sul, connect_cb, 100);
  while (n >= 0 && !interrupted)
    n = lws_service(context, 0);

  lws_context_destroy(context);
  return NULL;
}
//void *wsconfig_httpd_thread(void *ptr)

